package com.apex.shared.core.utils;

import lombok.experimental.UtilityClass;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.Date;
import java.util.List;
import java.util.stream.Stream;

/**
 * 日期时间工具类，基于 JDK 17+ 的新特性
 *
 * @author MoNaiHui
 */
@UtilityClass
public class DateTimeUtils {

    /**
     * 常用日期格式
     */
    public static final String PATTERN_DATE = "yyyy-MM-dd";
    public static final String PATTERN_TIME = "HH:mm:ss";
    public static final String PATTERN_DATETIME = "yyyy-MM-dd HH:mm:ss";
    public static final String PATTERN_DATETIME_MINUTE = "yyyy-MM-dd HH:mm";
    public static final String PATTERN_DATE_COMPACT = "yyyyMMdd";
    public static final String PATTERN_TIME_COMPACT = "HHmmss";
    public static final String PATTERN_DATETIME_COMPACT = "yyyyMMddHHmmss";

    /**
     * 常用时区
     */
    public static final ZoneId ZONE_BEIJING = ZoneId.of("Asia/Shanghai");
    public static final ZoneId ZONE_UTC = ZoneId.of("UTC");

    /**
     * 常用日期格式化器
     */
    public static final DateTimeFormatter FORMATTER_DATE = DateTimeFormatter.ofPattern(PATTERN_DATE);
    public static final DateTimeFormatter FORMATTER_TIME = DateTimeFormatter.ofPattern(PATTERN_TIME);
    public static final DateTimeFormatter FORMATTER_DATETIME = DateTimeFormatter.ofPattern(PATTERN_DATETIME);
    public static final DateTimeFormatter FORMATTER_DATETIME_MINUTE = DateTimeFormatter.ofPattern(PATTERN_DATETIME_MINUTE);

    // ---------------------- 获取当前日期时间 ----------------------

    /**
     * 获取当前日期时间（带时区）
     */
    public static ZonedDateTime getCurrentZonedDateTime() {
        return ZonedDateTime.now(ZONE_BEIJING);
    }

    /**
     * 获取当前日期时间（带纳秒）
     */
    public static Instant getCurrentInstant() {
        return Instant.now();
    }

    /**
     * 获取当前日期
     */
    public static LocalDate getCurrentDate() {
        return LocalDate.now(ZONE_BEIJING);
    }

    /**
     * 获取当前时间
     */
    public static LocalTime getCurrentTime() {
        return LocalTime.now(ZONE_BEIJING);
    }

    /**
     * 获取当前日期时间
     */
    public static LocalDateTime getCurrentDateTime() {
        return LocalDateTime.now(ZONE_BEIJING);
    }

    /**
     * 获取当前时间戳（毫秒）
     */
    public static long getCurrentTimestamp() {
        return Instant.now().toEpochMilli();
    }

    /**
     * 获取当前时间戳（秒）
     */
    public static long getCurrentTimestampSeconds() {
        return Instant.now().getEpochSecond();
    }

    // ---------------------- 日期时间转换 ----------------------

    /**
     * Date 转 LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZONE_BEIJING);
    }

    /**
     * LocalDateTime 转 Date
     */
    public static Date toDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZONE_BEIJING).toInstant());
    }

    /**
     * 时间戳转LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(long timestamp) {
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZONE_BEIJING);
    }

    /**
     * LocalDateTime转时间戳
     */
    public static long toTimestamp(LocalDateTime localDateTime) {
        return localDateTime.atZone(ZONE_BEIJING).toInstant().toEpochMilli();
    }

    // ---------------------- 日期时间格式化 ----------------------

    /**
     * 格式化日期时间为字符串
     */
    public static String format(LocalDateTime dateTime, String pattern) {
        return dateTime.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 格式化日期为字符串
     */
    public static String format(LocalDate date, String pattern) {
        return date.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 格式化时间为字符串
     */
    public static String format(LocalTime time, String pattern) {
        return time.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 将字符串解析为LocalDateTime
     */
    public static LocalDateTime parseDateTime(String dateTimeStr, String pattern) {
        return LocalDateTime.parse(dateTimeStr, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 将字符串解析为LocalDate
     */
    public static LocalDate parseDate(String dateStr, String pattern) {
        return LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 将字符串解析为LocalTime
     */
    public static LocalTime parseTime(String timeStr, String pattern) {
        return LocalTime.parse(timeStr, DateTimeFormatter.ofPattern(pattern));
    }

    // ---------------------- 日期时间计算 ----------------------

    /**
     * 计算两个日期时间之间的差（毫秒）
     */
    public static Duration between(LocalDateTime start, LocalDateTime end) {
        return Duration.between(start, end);
    }

    /**
     * 计算两个日期之间的天数
     */
    public static Period betweenDays(LocalDate start, LocalDate end) {
        return Period.between(start, end);
    }

    /**
     * 计算两个时间之间的差（格式化输出）
     */
    public static String formatDuration(Duration duration) {
        long days = duration.toDays();
        long hours = duration.toHoursPart();
        long minutes = duration.toMinutesPart();
        long seconds = duration.toSecondsPart();
        long millis = duration.toMillisPart();
        
        return String.format("%d天%d小时%d分%d秒%d毫秒", days, hours, minutes, seconds, millis);
    }

    /**
     * 日期时间加法
     */
    public static LocalDateTime plus(LocalDateTime dateTime, long amount, ChronoUnit unit) {
        return switch (unit) {
            case NANOS -> dateTime.plusNanos(amount);
            case MICROS -> dateTime.plus(amount, ChronoUnit.MICROS);
            case MILLIS -> dateTime.plus(amount, ChronoUnit.MILLIS);
            case SECONDS -> dateTime.plusSeconds(amount);
            case MINUTES -> dateTime.plusMinutes(amount);
            case HOURS -> dateTime.plusHours(amount);
            case DAYS -> dateTime.plusDays(amount);
            case WEEKS -> dateTime.plusWeeks(amount);
            case MONTHS -> dateTime.plusMonths(amount);
            case YEARS -> dateTime.plusYears(amount);
            default -> throw new IllegalArgumentException("Unsupported unit: " + unit);
        };
    }

    /**
     * 日期时间减法
     */
    public static LocalDateTime minus(LocalDateTime dateTime, long amount, ChronoUnit unit) {
        return switch (unit) {
            case NANOS -> dateTime.minusNanos(amount);
            case MICROS -> dateTime.minus(amount, ChronoUnit.MICROS);
            case MILLIS -> dateTime.minus(amount, ChronoUnit.MILLIS);
            case SECONDS -> dateTime.minusSeconds(amount);
            case MINUTES -> dateTime.minusMinutes(amount);
            case HOURS -> dateTime.minusHours(amount);
            case DAYS -> dateTime.minusDays(amount);
            case WEEKS -> dateTime.minusWeeks(amount);
            case MONTHS -> dateTime.minusMonths(amount);
            case YEARS -> dateTime.minusYears(amount);
            default -> throw new IllegalArgumentException("Unsupported unit: " + unit);
        };
    }

    // ---------------------- 日期时间判断 ----------------------

    /**
     * 判断是否为闰年
     */
    public static boolean isLeapYear(LocalDate date) {
        return date.isLeapYear();
    }

    /**
     * 判断是否为周末
     */
    public static boolean isWeekend(LocalDate date) {
        return switch (date.getDayOfWeek()) {
            case SATURDAY, SUNDAY -> true;
            default -> false;
        };
    }

    /**
     * 判断是否为工作日
     */
    public static boolean isWorkday(LocalDate date) {
        return !isWeekend(date);
    }

    /**
     * 判断两个日期是否为同一天
     */
    public static boolean isSameDay(LocalDateTime date1, LocalDateTime date2) {
        return date1.toLocalDate().equals(date2.toLocalDate());
    }

    // ---------------------- 特殊日期时间获取 ----------------------

    /**
     * 获取月初第一天
     */
    public static LocalDate getFirstDayOfMonth(LocalDate date) {
        return date.with(TemporalAdjusters.firstDayOfMonth());
    }

    /**
     * 获取月末最后一天
     */
    public static LocalDate getLastDayOfMonth(LocalDate date) {
        return date.with(TemporalAdjusters.lastDayOfMonth());
    }

    /**
     * 获取下一个工作日
     */
    public static LocalDate getNextWorkday(LocalDate date) {
        LocalDate nextDay = date.plusDays(1);
        while (isWeekend(nextDay)) {
            nextDay = nextDay.plusDays(1);
        }
        return nextDay;
    }

    /**
     * 获取本周第一天（周一）
     */
    public static LocalDate getFirstDayOfWeek(LocalDate date) {
        return date.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
    }

    /**
     * 获取本周最后一天（周日）
     */
    public static LocalDate getLastDayOfWeek(LocalDate date) {
        return date.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
    }

    /**
     * 获取当天开始时间
     */
    public static LocalDateTime getStartOfDay(LocalDate date) {
        return date.atStartOfDay();
    }

    /**
     * 获取当天结束时间
     */
    public static LocalDateTime getEndOfDay(LocalDate date) {
        return date.atTime(LocalTime.MAX);
    }

    /**
     * 获取本季度的第一天
     */
    public static LocalDate getFirstDayOfQuarter(LocalDate date) {
        int month = (date.getMonthValue() - 1) / 3 * 3 + 1;
        return LocalDate.of(date.getYear(), month, 1);
    }

    /**
     * 获取本季度的最后一天
     */
    public static LocalDate getLastDayOfQuarter(LocalDate date) {
        int month = (date.getMonthValue() - 1) / 3 * 3 + 3;
        return LocalDate.of(date.getYear(), month, 1)
                .with(TemporalAdjusters.lastDayOfMonth());
    }

    /**
     * 获取指定日期所在周的工作日列表（周一至周五）
     */
    public static List<LocalDate> getWorkdaysOfWeek(LocalDate date) {
        LocalDate monday = date.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
        return Stream.iterate(monday, d -> d.plusDays(1))
                .limit(5)
                .toList();
    }
} 